﻿using Avalonia;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Platform;

using HorizontalAlignment = Avalonia.Layout.HorizontalAlignment;
using VerticalAlignment = Avalonia.Layout.VerticalAlignment;

namespace Bread.Mvc.Avalonia;

public class VideoView : NativeControlHost
{
    public event Action? HwndCreated;

    public nint Hwnd { get; private set; } = nint.Zero;

    static VideoView()
    {
        IsVisibleProperty.Changed.AddClassHandler<VideoView>((s, e) => s.ShowOverlay(s.IsVisible));
        BoundsProperty.Changed.AddClassHandler<VideoView>((s, e) => s.UpdateOverlayPosition());
    }

    public static readonly StyledProperty<object?> ContentProperty =
        ContentControl.ContentProperty.AddOwner<VideoView>();

    [Content]
    public object? Content
    {
        get => GetValue(ContentProperty);
        set => SetValue(ContentProperty, value);
    }

    private bool _attached;
    private Window? _floatingContent;
    private IDisposable? _disposables;


    public VideoView()
    {
    }

    protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
    {
        var handle = base.CreateNativeControlCore(parent);
        Hwnd = handle.Handle;
        HwndCreated?.Invoke();
        return handle!;
    }

    protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnAttachedToVisualTree(e);

        _attached = true;

        if (_floatingContent == null) {
            CreateOverlayWindow();
        }
        ShowOverlay(true);

        var win = TopLevel.GetTopLevel(this) as Window;
        if (win != null) {
            win.PositionChanged += (s, e) => UpdateOverlayPosition();
        }
    }

    protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnDetachedFromVisualTree(e);
        ShowOverlay(false);
        _attached = false;
    }

    protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
    {
        base.OnDetachedFromLogicalTree(e);
        _disposables?.Dispose();
        _disposables = null;
        _floatingContent?.Close();
        _floatingContent = null;
    }

    private void CreateOverlayWindow()
    {
        if (_floatingContent != null) return;

        _floatingContent = new Window() {
            SystemDecorations = SystemDecorations.None,
            TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent },
            Background = Brushes.Transparent,
            CanResize = false,
            ShowInTaskbar = false
        };

        var owner = TopLevel.GetTopLevel(this) as Window;
        if (owner != null) {
            _floatingContent.Show(owner);
            _floatingContent.FontFamily = owner.FontFamily;
            _floatingContent.FontSize = owner.FontSize;
            _floatingContent.Foreground = owner.Foreground;
        }

        UpdateContent();
        ContentProperty.Changed.AddClassHandler<VideoView>((s, e) => s.UpdateContent());
    }

    private void UpdateContent()
    {
        if (this.Content != null && _floatingContent != null) {
            _floatingContent.Content = this.Content;
            this.Content = null;
        }
    }

    private void ShowOverlay(bool show)
    {
        if (_floatingContent == null) return;
        if (_floatingContent.IsVisible == show) return;

        var win = TopLevel.GetTopLevel(this) as Window;
        if (win == null) return;

        if (show && _attached) {
            _floatingContent.Show(win);
        }
        else
            _floatingContent.Hide();
    }

    private void UpdateOverlayPosition()
    {
        if (_floatingContent == null) return;

        bool forceSetWidth = false, forceSetHeight = false;
        var topLeft = new Point();
        var child = _floatingContent.Presenter?.Child;

        if (child?.IsArrangeValid == true) {
            switch (child.HorizontalAlignment) {
                case HorizontalAlignment.Right:
                    topLeft = topLeft.WithX(Bounds.Width - _floatingContent.Bounds.Width);
                    break;

                case HorizontalAlignment.Center:
                    topLeft = topLeft.WithX((Bounds.Width - _floatingContent.Bounds.Width) / 2);
                    break;

                case HorizontalAlignment.Stretch:
                    forceSetWidth = true;
                    break;
            }

            switch (child.VerticalAlignment) {
                case VerticalAlignment.Bottom:
                    topLeft = topLeft.WithY(Bounds.Height - _floatingContent.Bounds.Height);
                    break;

                case VerticalAlignment.Center:
                    topLeft = topLeft.WithY((Bounds.Height - _floatingContent.Bounds.Height) / 2);
                    break;

                case VerticalAlignment.Stretch:
                    forceSetHeight = true;
                    break;
            }
        }

        if (forceSetWidth && forceSetHeight)
            _floatingContent.SizeToContent = SizeToContent.Manual;
        else if (forceSetHeight)
            _floatingContent.SizeToContent = SizeToContent.Width;
        else if (forceSetWidth)
            _floatingContent.SizeToContent = SizeToContent.Height;
        else
            _floatingContent.SizeToContent = SizeToContent.Manual;

        _floatingContent.Width = forceSetWidth ? Bounds.Width : double.NaN;
        _floatingContent.Height = forceSetHeight ? Bounds.Height : double.NaN;

        _floatingContent.MaxWidth = Bounds.Width;
        _floatingContent.MaxHeight = Bounds.Height;

        var newPosition = this.PointToScreen(topLeft);

        if (newPosition != _floatingContent.Position) {
            _floatingContent.Position = newPosition;
        }
    }

}
